home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Utilities / PalmLink / src / PL_File.c < prev    next >
C/C++ Source or Header  |  2000-05-06  |  32KB  |  1,239 lines

  1. /**
  2.  * PalmLink -- Connect 3Com Palm with Amiga
  3.  *
  4.  * prc/pdb file access
  5.  *
  6.  * (C) 1998-2000 Richard Körber <rkoerber@gmx.de>
  7.  *
  8.  *------------------------------------------------------------------
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  *
  24.  * You must not use this source code to gain profit of any kind!
  25.  */
  26.  
  27.  
  28. #include <string.h>
  29. #include <utility/date.h>
  30. #include "palmlink_glob.h"
  31.  
  32.  
  33. #define TIME_DIFF (2335305600)    /* Seconds between 1.Jan.1978 and 1.Jan.1904 */
  34.  
  35. #define MKTAG(a,b,c,d)  ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))
  36.  
  37. __saveds __asm int PL_FileClose(register __a0 APTR fh);
  38.  
  39.  
  40. /*------------------------------------------------------**
  41. ** Name:        fileLen                          private
  42. **
  43. ** Funktion:    Ermittelt die Länge einer Datei
  44. **
  45. ** Parameter:   fh        Dateihandle
  46. ** Ergebnis:    len       Dateilänge oder -1: Fehler
  47. //>
  48. ** Bemerkungen:
  49. **
  50. ** Revision:    14. Juni 1998, 18:45:19
  51. */
  52. static LONG fileLen(BPTR fh)
  53. {
  54.   struct FileInfoBlock *fib;
  55.   LONG size;
  56.  
  57.   if(fib = AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
  58.   {
  59.     if(ExamineFH(fh,fib))
  60.     {
  61.       size = fib->fib_Size;
  62.       FreeVec(fib);
  63.       return(size);
  64.     }
  65.     FreeVec(fib);
  66.   }
  67.   return(-1);
  68. }
  69. //<
  70. /*------------------------------------------------------**
  71. ** Name:        unpackTime                       private
  72. **
  73. ** Funktion:    Datei -> Palm-Zeit
  74. **
  75. ** Parameter:   time      Sekunden seit 1.1.1904
  76. **              tstr      auszufüllende DLP_SysTime
  77. //>
  78. ** Bemerkungen:
  79. **
  80. ** Revision:    14. Juni 1998, 18:44:30
  81. */
  82. static void unpackTime(ULONG time, struct DLP_SysTime *tstr)
  83. {
  84.   if(time==0 || time==TIME_DIFF)      // TIME_DIFF is a workaround to catch buggy
  85.   {                                   // files made by palmlink below V1
  86.     tstr->year   = 0;
  87.     tstr->month  = 0;
  88.     tstr->day    = 0;
  89.     tstr->hour   = 0;
  90.     tstr->minute = 0;
  91.     tstr->second = 0;
  92.     tstr->pad    = 0;
  93.   }
  94.   else
  95.   {
  96.     struct ClockData cld;
  97.     Amiga2Date((time-TIME_DIFF),&cld);
  98.     tstr->year   = cld.year;
  99.     tstr->month  = cld.month;
  100.     tstr->day    = cld.mday;
  101.     tstr->hour   = cld.hour;
  102.     tstr->minute = cld.min;
  103.     tstr->second = cld.sec;
  104.   }
  105. }
  106. //<
  107. /*------------------------------------------------------**
  108. ** Name:        packTime                         private
  109. **
  110. ** Funktion:    Datei <- Palm-Zeit
  111. **
  112. ** Parameter:   tstr      DLP_SysTime
  113. **              time      auszufüllende Sekunden seit 1.1.1904
  114. //>
  115. ** Bemerkungen:
  116. **
  117. ** Revision:    14. Juni 1998, 18:44:30
  118. */
  119. static void packTime(struct DLP_SysTime *tstr, ULONG *time)
  120. {
  121.   if(tstr->year==0)
  122.   {
  123.     *time = 0;
  124.   }
  125.   else
  126.   {
  127.     struct ClockData cld;
  128.     cld.year  = tstr->year;
  129.     cld.month = tstr->month;
  130.     cld.mday  = tstr->day;
  131.     cld.hour  = tstr->hour;
  132.     cld.min   = tstr->minute;
  133.     cld.sec   = tstr->second;
  134.     *time = Date2Amiga(&cld) + TIME_DIFF;
  135.   }
  136. }
  137. //<
  138. /*------------------------------------------------------**
  139. ** Name:        createNewEntry                   private
  140. **
  141. ** Funktion:    Erzeugt ein neues Entry und hängt an
  142. **
  143. ** Parameter:   plfh      FileHandle
  144. ** Ergebnis:    entry     Neuer Entry
  145. //>
  146. ** Bemerkungen:
  147. **
  148. ** Revision:    14. Juni 1998, 20:29:37
  149. */
  150. static struct PL_Entry *createNewEntry(struct PL_File *plfh)
  151. {
  152.   struct PL_Entry *newentry = AllocVec(sizeof(struct PL_Entry),MEMF_PUBLIC|MEMF_CLEAR);
  153.   if(!newentry) return(NULL);
  154.  
  155.   AddTail((struct List *)&plfh->entries, (struct Node *)newentry);
  156.   plfh->cntEntries++;
  157.   return(newentry);
  158. }
  159. //<
  160. /*------------------------------------------------------**
  161. ** Name:        getEntry                         private
  162. **
  163. ** Funktion:    Holt den Eintrag nach Index
  164. **
  165. ** Parameter:   plfh      Filehandle
  166. **              ix        Index
  167. ** Ergebnis:    entry     Eintrag oder NULL
  168. //>
  169. ** Bemerkungen:
  170. **
  171. ** Revision:    14. Juni 1998, 20:43:43
  172. */
  173. static struct PL_Entry *getEntry(struct PL_File *plfh, UWORD ix)
  174. {
  175.   struct PL_Entry *currEntry, *nextEntry;
  176.  
  177.   for
  178.   (
  179.     currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  180.     nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  181.     currEntry=nextEntry
  182.   )
  183.   {
  184.     if(!(ix--)) return(currEntry);
  185.   }
  186.   return(NULL);
  187. }
  188. //<
  189.  
  190.  
  191.  
  192. /*------------------------------------------------------**
  193. ** Name:        PL_FileOpen                       public
  194. **
  195. ** Funktion:    Öffnet eine Palm-Datei
  196. **
  197. ** Parameter:   name      Dateiname
  198. **              dbinfo    DBInfo (NULL: read, sonst write)
  199. ** Ergebnis:    fh        Filehandle
  200. //>
  201. ** Bemerkungen:
  202. **
  203. ** Revision:    13. Juni 1998, 22:59:05
  204. */
  205. __saveds __asm APTR PL_FileOpen
  206. (
  207.   register __a0 STRPTR name,
  208.   register __a1 struct DLP_DBInfo *ndbinfo
  209. )
  210. {
  211.   struct PL_File *plfh;
  212.  
  213.   if(!(plfh = AllocVec(sizeof(struct PL_File),MEMF_PUBLIC|MEMF_CLEAR))) return(NULL);   // NoMem
  214.   NewList((struct List *)&plfh->entries);
  215.  
  216.   if(!ndbinfo)
  217.   {
  218.     /*---- OPEN FILE FOR READING -------------------------------------*/
  219.     struct PL_FileHeader hdr;
  220.     struct DLP_DBInfo *dbi = &plfh->dbinfo;
  221.     struct PL_ResEntryHeader reseh;
  222.     struct PL_RecEntryHeader receh;
  223.     struct PL_Entry *currEntry, *predEntry;
  224.     ULONG oldoffset;
  225.     UWORD i;
  226.  
  227.     /* Open file */
  228.     if(!(plfh->fh = Open(name,MODE_OLDFILE))) goto error;
  229.     plfh->size = fileLen(plfh->fh);
  230.     /* parse header */
  231.     if(FRead(plfh->fh,&hdr,sizeof(struct PL_FileHeader),1) != 1) goto error;
  232.     CopyMem(hdr.name,dbi->name,32);
  233.     dbi->name[32] = '\0';
  234.     dbi->flags              = hdr.flags;
  235.     dbi->version            = hdr.version;
  236.     unpackTime(hdr.creationTime,&dbi->createDate);
  237.     unpackTime(hdr.modificationTime,&dbi->modifyDate);
  238.     unpackTime(hdr.backupTime,&dbi->backupDate);
  239.     dbi->modnum             = hdr.modificationNr;
  240.     dbi->type               = hdr.type;
  241.     dbi->creator            = hdr.creator;
  242.     plfh->uniqueID          = hdr.uniqueID;
  243.     plfh->nextRecordListID  = hdr.nextRecordListID;
  244.     plfh->numRecords        = hdr.numRecords;
  245.     if(plfh->nextRecordListID) goto error;       // Extended Format is not supported
  246.  
  247. #ifdef FILEDEBUG
  248.   Printf("Entries: %ld\n",plfh->numRecords);
  249. #endif
  250.  
  251.     if(dbi->flags & DLPDBIF_RESOURCE)
  252.     {
  253.       plfh->resourceFlag  = TRUE;
  254.       plfh->entHeaderSize = sizeof(struct PL_ResEntryHeader);
  255.     }
  256.     else
  257.     {
  258.       plfh->resourceFlag  = FALSE;
  259.       plfh->entHeaderSize = sizeof(struct PL_RecEntryHeader);
  260.     }
  261.  
  262.     if(plfh->numRecords)                    // Entries are present?
  263.     {
  264.       for(i=0; i<plfh->numRecords; i++)
  265.       {
  266.         currEntry = createNewEntry(plfh);
  267.         if(!currEntry) goto error;
  268.         if(plfh->resourceFlag)
  269.         {
  270.           if(FRead(plfh->fh,&reseh,sizeof(struct PL_ResEntryHeader),1) != 1) goto error;
  271.           currEntry->type    = reseh.type;
  272.           currEntry->id      = reseh.id;
  273.           currEntry->offset  = reseh.offset;
  274.         }
  275.         else
  276.         {
  277.           if(FRead(plfh->fh,&receh,sizeof(struct PL_RecEntryHeader),1) != 1) goto error;
  278.           currEntry->offset  = receh.offset;
  279.           currEntry->attrs   = receh.attrs;
  280.           currEntry->uid     = (((ULONG)(receh.uid[0]))<<16) + (((ULONG)(receh.uid[1]))<<8) + ((ULONG)(receh.uid[2]));
  281.         }
  282.       }
  283.  
  284.       /* Get lengths */
  285.       oldoffset = plfh->size;
  286.       for
  287.       (
  288.         currEntry=(struct PL_Entry *)plfh->entries.mlh_TailPred;
  289.         predEntry=(struct PL_Entry *)currEntry->node.mln_Pred;
  290.         currEntry=predEntry
  291.       )
  292.       {
  293.         currEntry->size = oldoffset - currEntry->offset;
  294.         oldoffset = currEntry->offset;
  295.       }
  296.  
  297.       if(hdr.sortInfoOffset)
  298.       {
  299.         plfh->sortInfoSize = oldoffset - hdr.sortInfoOffset;
  300.         oldoffset = hdr.sortInfoOffset;
  301.       }
  302.  
  303.       if(hdr.appInfoOffset)
  304.       {
  305.         plfh->appInfoSize = oldoffset - hdr.appInfoOffset;
  306.         oldoffset = hdr.appInfoOffset;
  307.       }
  308.  
  309.       /* Read AppInfo */
  310.       if(plfh->appInfoSize)
  311.       {
  312. #ifdef FILEDEBUG
  313.   Printf("AppInfo @0x%08lx (Size %ld)\n",hdr.appInfoOffset,plfh->appInfoSize);
  314. #endif
  315.         if(!(plfh->appInfo = AllocVec(plfh->appInfoSize,MEMF_PUBLIC))) goto error;
  316.         Seek(plfh->fh,hdr.appInfoOffset,OFFSET_BEGINNING);  /** ERROR ? **/
  317.         Flush(plfh->fh);
  318.         if(FRead(plfh->fh,plfh->appInfo,plfh->appInfoSize,1) != 1) goto error;
  319.       }
  320.       else
  321.       {
  322.         plfh->appInfo = NULL;
  323.       }
  324.  
  325.       /* Read SortInfo */
  326.       if(plfh->sortInfoSize)
  327.       {
  328. #ifdef FILEDEBUG
  329.   Printf("SortInfo @0x%08lx (Size %ld)\n",hdr.sortInfoOffset,plfh->sortInfoSize);
  330. #endif
  331.         if(!(plfh->sortInfo = AllocVec(plfh->sortInfoSize,MEMF_PUBLIC))) goto error;
  332.         Seek(plfh->fh,hdr.sortInfoOffset,OFFSET_BEGINNING);  /** ERROR ? **/
  333.         Flush(plfh->fh);
  334.         if(FRead(plfh->fh,plfh->sortInfo,plfh->sortInfoSize,1) != 1) goto error;
  335.       }
  336.       else
  337.       {
  338.         plfh->sortInfo = NULL;
  339.       }
  340.  
  341.       /* Read each entry */
  342.       for
  343.       (
  344.         currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  345.         predEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  346.         currEntry=predEntry
  347.       )
  348.       {
  349. #ifdef FILEDEBUG
  350.   Printf("Entry @0x%08lx (Size %ld)\n",currEntry->offset,currEntry->size);
  351. #endif
  352.         if(!(currEntry->entryData = AllocVec(currEntry->size,MEMF_PUBLIC))) goto error;
  353.         Seek(plfh->fh,currEntry->offset,OFFSET_BEGINNING);
  354.         Flush(plfh->fh);
  355.         if(FRead(plfh->fh,currEntry->entryData,currEntry->size,1) != 1) goto error;
  356. #ifdef FILEDEBUG
  357.   Printf("  Data %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",((ULONG*)currEntry->entryData)[0],((ULONG*)currEntry->entryData)[1],((ULONG*)currEntry->entryData)[2],((ULONG*)currEntry->entryData)[3],((ULONG*)currEntry->entryData)[4],((ULONG*)currEntry->entryData)[5],((ULONG*)currEntry->entryData)[6],((ULONG*)currEntry->entryData)[7]);
  358. #endif
  359.       }
  360.     }
  361.  
  362.     return(plfh);
  363.   }
  364.   else
  365.   {
  366.     /*---- OPEN FILE FOR WRITING -------------------------------------*/
  367.     if(!(plfh->filename = AllocVec(strlen(name)+1, MEMF_PUBLIC))) goto error;
  368.     CopyMem(name,plfh->filename,strlen(name)+1);
  369.  
  370.     plfh->forWriteFlag = TRUE;
  371.     CopyMem(ndbinfo,&plfh->dbinfo,sizeof(struct DLP_DBInfo));
  372.     if(plfh->dbinfo.flags & DLPDBIF_RESOURCE)
  373.     {
  374.       plfh->resourceFlag  = TRUE;
  375.       plfh->entHeaderSize = sizeof(struct PL_ResEntryHeader);
  376.     }
  377.     else
  378.     {
  379.       plfh->resourceFlag  = FALSE;
  380.       plfh->entHeaderSize = sizeof(struct PL_RecEntryHeader);
  381.     }
  382.  
  383.     return(plfh);
  384.   }
  385. error:
  386.  
  387.   plfh->forWriteFlag = FALSE;
  388.   PL_FileClose(plfh);
  389.   return(NULL);
  390. }
  391. //<
  392. /*------------------------------------------------------**
  393. ** Name:        PL_FileClose                      public
  394. **
  395. ** Funktion:    Schließt eine Palm-Datei
  396. **
  397. ** Parameter:   fh        Filehandle
  398. ** Ergebnis:    success
  399. //>
  400. ** Bemerkungen:
  401. **
  402. ** Revision:    14. Juni 1998, 18:46:17
  403. */
  404. __saveds __asm int PL_FileClose
  405. (
  406.   register __a0 APTR fh
  407. )
  408. {
  409.   struct PL_File *plfh = (struct PL_File *)fh;
  410.   struct PL_Entry *currEntry, *nextEntry;
  411.   int res = TRUE;
  412.  
  413.   /* In write mode, write file */
  414.   if(plfh->forWriteFlag)
  415.   {
  416.     struct PL_FileHeader fhdr = {0};
  417.     struct DLP_DBInfo *dbi = &plfh->dbinfo;
  418.     struct PL_RecEntryHeader receh;
  419.     struct PL_ResEntryHeader reseh;
  420.     ULONG offset;
  421.  
  422.     /* Open File */
  423.     plfh->fh = Open(plfh->filename,MODE_NEWFILE);
  424.     if(!plfh->fh) { res=FALSE; goto error; }
  425.  
  426.     offset = sizeof(struct PL_FileHeader) + plfh->cntEntries*plfh->entHeaderSize + 2;
  427.  
  428.     /* Fill header */
  429.     strncpy(fhdr.name,dbi->name,32);
  430.     fhdr.flags            = dbi->flags;
  431.     fhdr.version          = dbi->version;
  432.     packTime(&dbi->createDate,&fhdr.creationTime);
  433.     packTime(&dbi->modifyDate,&fhdr.modificationTime);
  434.     packTime(&dbi->backupDate,&fhdr.backupTime);
  435.     fhdr.modificationNr   = dbi->modnum;
  436.     fhdr.type             = dbi->type;
  437.     fhdr.creator          = dbi->creator;
  438.     fhdr.uniqueID         = plfh->uniqueID;
  439.     fhdr.nextRecordListID = plfh->nextRecordListID;
  440.     fhdr.numRecords       = plfh->cntEntries;
  441.  
  442.     if(plfh->appInfo)
  443.     {
  444.       fhdr.appInfoOffset  = offset;
  445.       offset += plfh->appInfoSize;
  446.     }
  447.     if(plfh->sortInfo)
  448.     {
  449.       fhdr.sortInfoOffset = offset;
  450.       offset += plfh->sortInfoSize;
  451.     }
  452.  
  453.     if(FWrite(plfh->fh,&fhdr,sizeof(struct PL_FileHeader),1) != 1) {res=FALSE; goto error;}
  454.  
  455.     for
  456.     (
  457.       currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  458.       nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  459.       currEntry=nextEntry
  460.     )
  461.     {
  462.       currEntry->offset = offset;
  463.       if(plfh->resourceFlag)
  464.       {
  465.         reseh.type   = currEntry->type;
  466.         reseh.id     = currEntry->id;
  467.         reseh.offset = currEntry->offset;
  468.         if(FWrite(plfh->fh,&reseh,sizeof(struct PL_ResEntryHeader),1) != 1) {res=FALSE; goto error;}
  469.       }
  470.       else
  471.       {
  472.         receh.offset = currEntry->offset;
  473.         receh.attrs  = currEntry->attrs;
  474.         receh.uid[0] = (currEntry->uid>>16) & 0xFF;
  475.         receh.uid[1] = (currEntry->uid>>8) & 0xFF;
  476.         receh.uid[2] = currEntry->uid & 0xFF;
  477.         if(FWrite(plfh->fh,&receh,sizeof(struct PL_RecEntryHeader),1) != 1) {res=FALSE; goto error;}
  478.       }
  479.       offset += currEntry->size;
  480.     }
  481.     if(FWrite(plfh->fh,"\0\0",2,1) != 1) {res=FALSE; goto error;}
  482.  
  483.     if(plfh->appInfo)
  484.       if(FWrite(plfh->fh,plfh->appInfo,plfh->appInfoSize,1) != 1) {res=FALSE; goto error;}
  485.  
  486.     if(plfh->sortInfo)
  487.       if(FWrite(plfh->fh,plfh->sortInfo,plfh->sortInfoSize,1) != 1) {res=FALSE; goto error;}
  488.  
  489.     for
  490.     (
  491.       currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  492.       nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  493.       currEntry=nextEntry
  494.     )
  495.     {
  496.       if(FWrite(plfh->fh,currEntry->entryData,currEntry->size,1) != 1) {res=FALSE; goto error;}
  497.     }
  498.   }
  499.  
  500. error:
  501.   if(plfh->fh) Close(plfh->fh);                   // Close file
  502.  
  503.   /* Free all entries */
  504.   for
  505.   (
  506.     currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  507.     nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  508.     currEntry=nextEntry
  509.   )
  510.   {
  511.     if(currEntry->entryData) FreeVec(currEntry->entryData);
  512.     Remove((struct Node *)currEntry);
  513.     FreeVec(currEntry);
  514.   }
  515.  
  516.   /* Free memory */
  517.   if(plfh->appInfo)  FreeVec(plfh->appInfo);      // Free AppInfo
  518.   if(plfh->sortInfo) FreeVec(plfh->sortInfo);     // Free SortInfo
  519.   if(plfh->filename) FreeVec(plfh->filename);     // Copy file name
  520.  
  521.   FreeVec(plfh);                                  // Release handle itself
  522.   return(res);                                    // Done!
  523. }
  524. //<
  525.  
  526. /*------------------------------------------------------**
  527. ** Name:        PL_FileGetDBInfo
  528. **
  529. ** Funktion:    Holt sich die DBInfo
  530. **
  531. ** Parameter:   fh        Filehandle
  532. ** Ergebnis:    dbinfo
  533. //>
  534. ** Bemerkungen:
  535. **
  536. ** Revision:    14. Juni 1998, 18:57:20
  537. */
  538. __saveds __asm struct DLP_DBInfo *PL_FileGetDBInfo
  539. (
  540.   register __a0 APTR fh
  541. )
  542. {
  543.   return(&((struct PL_File *)fh)->dbinfo);
  544. }
  545. //<
  546. /*------------------------------------------------------**
  547. ** Name:        PL_FileSetDBInfo
  548. **
  549. ** Funktion:    Setzt die DBInfo
  550. **
  551. ** Parameter:   fh        Filehandle
  552. **              dbinfo    Zu setzende DBInfo
  553. ** Ergebnis:    success
  554. //>
  555. ** Bemerkungen:
  556. **
  557. ** Revision:    14. Juni 1998, 19:30:39
  558. */
  559. __saveds __asm int PL_FileSetDBInfo
  560. (
  561.   register __a0 APTR fh,
  562.   register __a1 struct DLP_DBInfo *dbinfo
  563. )
  564. {
  565.   struct PL_File *plfh = (struct PL_File *)fh;
  566.  
  567.   if(!plfh->forWriteFlag) return(FALSE);
  568.   if((dbinfo->flags&DLPDBIF_RESOURCE) != (plfh->dbinfo.flags&DLPDBIF_RESOURCE)) return(FALSE);
  569.   CopyMem(dbinfo,&plfh->dbinfo,sizeof(struct DLP_DBInfo));
  570.   return(TRUE);
  571. }
  572. //<
  573. /*------------------------------------------------------**
  574. ** Name:        PL_FileGetAppInfo
  575. **
  576. ** Funktion:    Holt sich Infos über die Application
  577. **
  578. ** Parameter:   fh        Filehandle
  579. **              size      Platz für die Größe
  580. ** Ergebnis:    appinfo
  581. //>
  582. ** Bemerkungen:
  583. **
  584. ** Revision:    14. Juni 1998, 18:57:20
  585. */
  586. __saveds __asm APTR PL_FileGetAppInfo
  587. (
  588.   register __a0 APTR fh,
  589.   register __a1 ULONG *size
  590. )
  591. {
  592.   struct PL_File *plfh = (struct PL_File *)fh;
  593.  
  594.   *size = plfh->appInfoSize;
  595.   return(plfh->appInfo);
  596. }
  597. //<
  598. /*------------------------------------------------------**
  599. ** Name:        PL_FileSetAppInfo
  600. **
  601. ** Funktion:    Setzt Infos über die Application
  602. **
  603. ** Parameter:   fh        Filehandle
  604. **              appinfo   Appinfo
  605. **              size      Größe
  606. ** Ergebnis:    success
  607. //>
  608. ** Bemerkungen:
  609. **
  610. ** Revision:    14. Juni 1998, 18:57:20
  611. */
  612. __saveds __asm int PL_FileSetAppInfo
  613. (
  614.   register __a0 APTR fh,
  615.   register __a1 APTR appinfo,
  616.   register __d0 ULONG size
  617. )
  618. {
  619.   struct PL_File *plfh = (struct PL_File *)fh;
  620.   APTR newappinfo;
  621.  
  622.   if(plfh->appInfo)
  623.   {
  624.     FreeVec(plfh->appInfo);
  625.     plfh->appInfo = NULL;
  626.     plfh->appInfoSize = 0;
  627.   }
  628.  
  629.   if(!appinfo) return(TRUE);
  630.  
  631.   if(newappinfo = AllocVec(size,MEMF_PUBLIC))
  632.   {
  633.     plfh->appInfo = newappinfo;
  634.     plfh->appInfoSize = size;
  635.     CopyMem(appinfo,newappinfo,size);
  636.     return(TRUE);
  637.   }
  638.   return(FALSE);
  639. }
  640. //<
  641. /*------------------------------------------------------**
  642. ** Name:        PL_FileGetSortInfo
  643. **
  644. ** Funktion:    Holt sich Infos über das Sorting
  645. **
  646. ** Parameter:   fh        Filehandle
  647. **              size      Platz für die Größe
  648. ** Ergebnis:    sortinfo
  649. //>
  650. ** Bemerkungen:
  651. **
  652. ** Revision:    14. Juni 1998, 19:00:39
  653. */
  654. __saveds __asm APTR PL_FileGetSortInfo
  655. (
  656.   register __a0 APTR fh,
  657.   register __a1 ULONG *size
  658. )
  659. {
  660.   struct PL_File *plfh = (struct PL_File *)fh;
  661.  
  662.   *size = plfh->sortInfoSize;
  663.   return(plfh->sortInfo);
  664. }
  665. //<
  666. /*------------------------------------------------------**
  667. ** Name:        PL_FileSetSortInfo
  668. **
  669. ** Funktion:    Setzt Infos über Sorting
  670. **
  671. ** Parameter:   fh        Filehandle
  672. **              sortinfo  Sortinfo
  673. **              size      Größe
  674. ** Ergebnis:    success
  675. //>
  676. ** Bemerkungen:
  677. **
  678. ** Revision:    14. Juni 1998, 18:57:20
  679. */
  680. __saveds __asm int PL_FileSetSortInfo
  681. (
  682.   register __a0 APTR fh,
  683.   register __a1 APTR sortinfo,
  684.   register __d0 ULONG size
  685. )
  686. {
  687.   struct PL_File *plfh = (struct PL_File *)fh;
  688.   APTR newsortinfo;
  689.  
  690.   if(plfh->sortInfo)
  691.   {
  692.     FreeVec(plfh->sortInfo);
  693.     plfh->sortInfo = NULL;
  694.     plfh->sortInfoSize = 0;
  695.   }
  696.  
  697.   if(!sortinfo) return(TRUE);
  698.  
  699.   if(newsortinfo = AllocVec(size,MEMF_PUBLIC))
  700.   {
  701.     plfh->sortInfo = newsortinfo;
  702.     plfh->sortInfoSize = size;
  703.     CopyMem(sortinfo,newsortinfo,size);
  704.     return(TRUE);
  705.   }
  706.   return(FALSE);
  707. }
  708. //<
  709.  
  710. /*------------------------------------------------------**
  711. ** Name:        PL_FileReadResource
  712. **
  713. ** Funktion:    Liest eine Resource
  714. **
  715. ** Parameter:   fh        Filehandle
  716. **              ix        Index
  717. **              size      Platz für Größe
  718. **              type      Platz für Typ
  719. **              id        Platz für ID
  720. ** Ergebnis:    resource  Gewünschte Resource, NULL: Error
  721. //>
  722. ** Bemerkungen:
  723. **
  724. ** Revision:    14. Juni 1998, 20:43:43
  725. */
  726. __saveds __asm APTR PL_FileReadResource
  727. (
  728.   register __a0 APTR fh,
  729.   register __d0 UWORD ix,
  730.   register __a1 ULONG *size,
  731.   register __a2 ULONG *type,
  732.   register __a3 UWORD *id
  733. )
  734. {
  735.   struct PL_File *plfh = (struct PL_File *)fh;
  736.   struct PL_Entry *ent;
  737.  
  738.   if(plfh->forWriteFlag || !plfh->resourceFlag) return(NULL);
  739.   ent = getEntry(plfh,ix);
  740.   if(!ent) return(NULL);
  741.  
  742.   if(size) *size = ent->size;
  743.   if(type) *type = ent->type;
  744.   if(id)   *id   = ent->id;
  745.   return(ent->entryData);
  746. }
  747. //<
  748. /*------------------------------------------------------**
  749. ** Name:        PL_FileAddResource
  750. **
  751. ** Funktion:    Fügt eine Resource hinzu
  752. **
  753. ** Parameter:   fh        Filehandle
  754. **              buffer    Daten für die Resource
  755. **              size      Größe
  756. **              type      Typ
  757. **              id        ID
  758. ** Ergebnis:    success
  759. //>
  760. ** Bemerkungen:
  761. **
  762. ** Revision:    14. Juni 1998, 21:14:48
  763. */
  764. __saveds __asm int PL_FileAddResource
  765. (
  766.   register __a0 APTR fh,
  767.   register __a1 APTR buffer,
  768.   register __d0 ULONG size,
  769.   register __d1 ULONG type,
  770.   register __d2 UWORD id
  771. )
  772. {
  773.   struct PL_File *plfh = (struct PL_File *)fh;
  774.   struct PL_Entry *ent;
  775.   APTR newbuf;
  776.  
  777.   if(!plfh->forWriteFlag || !plfh->resourceFlag) return(FALSE);
  778.   if(newbuf = AllocVec(size,MEMF_PUBLIC))
  779.   {
  780.     if(ent = createNewEntry(plfh))
  781.     {
  782.       CopyMem(buffer,newbuf,size);
  783.       ent->entryData = newbuf;
  784.       ent->size      = size;
  785.       ent->type      = type;
  786.       ent->id        = id;
  787.       return(TRUE);
  788.     }
  789.     FreeVec(newbuf);
  790.   }
  791.   return(FALSE);
  792. }
  793. //<
  794.  
  795. /*------------------------------------------------------**
  796. ** Name:        PL_FileReadRecord
  797. **
  798. ** Funktion:    Liest einen Record
  799. **
  800. ** Parameter:   fh        Filehandle
  801. **              ix        Index
  802. **              size      Platz für Größe
  803. **              attr      Platz für Attribute
  804. **              uid       Platz für UID
  805. ** Ergebnis:    resource  Gewünschte Resource, NULL: Error
  806. //>
  807. ** Bemerkungen:
  808. **
  809. ** Revision:    14. Juni 1998, 20:43:43
  810. */
  811. __saveds __asm APTR PL_FileReadRecord
  812. (
  813.   register __a0 APTR fh,
  814.   register __d0 UWORD ix,
  815.   register __a1 ULONG *size,
  816.   register __a2 UBYTE *attr,
  817.   register __a3 ULONG *uid
  818. )
  819. {
  820.   struct PL_File *plfh = (struct PL_File *)fh;
  821.   struct PL_Entry *ent;
  822.  
  823.   if(plfh->forWriteFlag || plfh->resourceFlag) return(NULL);
  824.   ent = getEntry(plfh,ix);
  825.   if(!ent) return(NULL);
  826.  
  827.   if(size) *size = ent->size;
  828.   if(attr) *attr = ent->attrs;
  829.   if(uid)  *uid  = ent->uid;
  830.   return(ent->entryData);
  831. }
  832. //<
  833. /*------------------------------------------------------**
  834. ** Name:        PL_FileReadRecordID
  835. **
  836. ** Funktion:    Liest einen Record
  837. **
  838. ** Parameter:   fh        Filehandle
  839. **              uid       UID
  840. **              size      Platz für Größe
  841. **              attr      Platz für Attribute
  842. **              ix        Platz für Index
  843. ** Ergebnis:    resource  Gewünschte Resource, NULL: Error
  844. //>
  845. ** Bemerkungen:
  846. **
  847. ** Revision:    14. Juni 1998, 20:43:43
  848. */
  849. __saveds __asm APTR PL_FileReadRecordID
  850. (
  851.   register __a0 APTR fh,
  852.   register __d0 ULONG uid,
  853.   register __a1 ULONG *size,
  854.   register __a2 UBYTE *attr,
  855.   register __a3 UWORD *ix
  856. )
  857. {
  858.   struct PL_File *plfh = (struct PL_File *)fh;
  859.   struct PL_Entry *currEntry, *nextEntry;
  860.   UWORD cix=0;
  861.  
  862.   for
  863.   (
  864.     currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  865.     nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  866.     currEntry=nextEntry
  867.   )
  868.   {
  869.     if(currEntry->uid == uid)
  870.     {
  871.       if(ix) *ix = cix;
  872.       return(PL_FileReadRecord(fh,cix,size,attr,NULL));
  873.     }
  874.     cix++;
  875.   }
  876.   return(NULL);
  877. }
  878. //<
  879. /*------------------------------------------------------**
  880. ** Name:        PL_FileAddRecord
  881. **
  882. ** Funktion:    Fügt einen Record hinzu
  883. **
  884. ** Parameter:   fh        Filehandle
  885. **              buffer    Daten für die Resource
  886. **              size      Größe
  887. **              attr      Attribute
  888. **              uid       Unique ID
  889. ** Ergebnis:    success
  890. //>
  891. ** Bemerkungen:
  892. **
  893. ** Revision:    14. Juni 1998, 21:14:48
  894. */
  895. __saveds __asm int PL_FileAddRecord
  896. (
  897.   register __a0 APTR fh,
  898.   register __a1 APTR buffer,
  899.   register __d0 ULONG size,
  900.   register __d1 UBYTE attr,
  901.   register __d2 ULONG uid
  902. )
  903. {
  904.   struct PL_File *plfh = (struct PL_File *)fh;
  905.   struct PL_Entry *ent;
  906.   APTR newbuf;
  907.  
  908.   if(!plfh->forWriteFlag || plfh->resourceFlag) return(FALSE);
  909.   if(newbuf = AllocVec(size,MEMF_PUBLIC))
  910.   {
  911.     if(ent = createNewEntry(plfh))
  912.     {
  913.       CopyMem(buffer,newbuf,size);
  914.       ent->entryData = newbuf;
  915.       ent->size      = size;
  916.       ent->attrs     = attr;
  917.       ent->uid       = uid;
  918.       return(TRUE);
  919.     }
  920.     FreeVec(newbuf);
  921.   }
  922.   return(FALSE);
  923. }
  924. //<
  925.  
  926. /*------------------------------------------------------**
  927. ** Name:        PL_FileUsedID
  928. **
  929. ** Funktion:    Testet, ob UID benutzt wird
  930. **
  931. ** Parameter:   fh        Filehandle
  932. **              uid       UID
  933. ** Ergebnis:    result    TRUE: wird benutzt
  934. //>
  935. ** Bemerkungen:
  936. **
  937. ** Revision:    14. Juni 1998, 20:43:43
  938. */
  939. __saveds __asm int PL_FileUsedID
  940. (
  941.   register __a0 APTR fh,
  942.   register __d0 ULONG uid
  943. )
  944. {
  945.   struct PL_File *plfh = (struct PL_File *)fh;
  946.   struct PL_Entry *currEntry, *nextEntry;
  947.  
  948.   for
  949.   (
  950.     currEntry=(struct PL_Entry *)plfh->entries.mlh_Head;
  951.     nextEntry=(struct PL_Entry *)currEntry->node.mln_Succ;
  952.     currEntry=nextEntry
  953.   )
  954.   {
  955.     if(currEntry->uid == uid) return(TRUE);
  956.   }
  957.   return(FALSE);
  958. }
  959. //<
  960.  
  961.  
  962. /*------------------------------------------------------**
  963. ** Name:        PL_FileRetrieve
  964. **
  965. ** Funktion:    Empfängt eine Datei vom Palm
  966. **
  967. ** Parameter:   fh        Filehandle
  968. **              socket    Socket
  969. **              cardno    Karten-Nr
  970. ** Ergebnis:    success
  971. //>
  972. ** Bemerkungen:
  973. **
  974. ** Revision:    14. Juni 1998, 23:05:50
  975. */
  976. __saveds __asm int PL_FileRetrieve
  977. (
  978.   register __a0 APTR fh,
  979.   register __a1 APTR socket,
  980.   register __d0 UBYTE cardno
  981. )
  982. {
  983.   struct PL_File *plfh = (struct PL_File *)fh;
  984.   APTR buffer;
  985.   LONG dbh, length, entries;
  986.   WORD i;
  987.   ULONG type,uid;
  988.   UWORD id,size;
  989.   UBYTE attr,category;
  990.   int res=FALSE;
  991.  
  992.   buffer = AllocVec(0x10000,MEMF_PUBLIC);
  993.   if(buffer)
  994.   {
  995.     dbh = DLP_OpenDB(socket, cardno, DLPDBOF_READ|DLPDBOF_SECRET, plfh->dbinfo.name);
  996.     if(dbh>=0)
  997.     {
  998.       length = DLP_ReadAppBlock(socket,dbh,0,buffer,0xFFFF);
  999.       if(length>0)
  1000.       {
  1001.         PL_FileSetAppInfo(fh,buffer,length);
  1002.       }
  1003.  
  1004.       entries = DLP_CountDBEntries(socket,dbh);
  1005.       if(entries>=0)
  1006.       {
  1007.         if(plfh->resourceFlag)
  1008.         {
  1009.           /*--- Resource ---*/
  1010.           for(i=0; i<entries; i++)
  1011.           {
  1012.             if(DLP_ReadResourceByIndex(socket,dbh,i,buffer,&type,&id,&size) >= 0)
  1013.             {
  1014.               if(!PL_FileAddResource(fh,buffer,size,type,id)) goto error;
  1015.             }
  1016.           }
  1017.           res = TRUE;
  1018.         }
  1019.         else
  1020.         {
  1021.           /*--- Record ---*/
  1022.           for(i=0; i<entries; i++)
  1023.           {
  1024.             if(DLP_ReadRecordByIndex(socket,dbh,i,buffer,&uid,&size,&attr,&category) >= 0)
  1025.             {
  1026.               if(attr & (DLPDBAT_DELETED|DLPDBAT_ARCHIVE)) continue;  // Cannot be restored
  1027.               if(!PL_FileAddRecord(fh,buffer,size,((attr&0xF0) | (category&0x0F)),uid)) goto error;
  1028.             }
  1029.           }
  1030.           res = TRUE;
  1031.         }
  1032.       }
  1033. error:
  1034.       DLP_CloseDB(socket,dbh);
  1035.     }
  1036.     FreeVec(buffer);
  1037.   }
  1038.   return(res);
  1039. }
  1040. //<
  1041. /*------------------------------------------------------**
  1042. ** Name:        PL_FileInstall
  1043. **
  1044. ** Funktion:    Sendet eine Datei zum Palm
  1045. **
  1046. ** Parameter:   fh        Filehandle
  1047. **              socket    Socket
  1048. **              cardno    Karten-Nr
  1049. ** Ergebnis:    success
  1050. //>
  1051. ** Bemerkungen:
  1052. **
  1053. ** Revision:    14. Juni 1998, 23:05:50
  1054. */
  1055. __saveds __asm int PL_FileInstall
  1056. (
  1057.   register __a0 APTR fh,
  1058.   register __a1 APTR socket,
  1059.   register __d0 UBYTE cardno
  1060. )
  1061. {
  1062.   struct PL_File *plfh = (struct PL_File *)fh;
  1063.   struct PL_Socket *sock = (struct PL_Socket *)socket;
  1064.   int  res = FALSE;
  1065.   BOOL reset = FALSE;
  1066.   LONG dbh;
  1067.   UWORD tmpflags, id;
  1068.   APTR appinfo, appinfo2=NULL, buffer;
  1069.   ULONG size,type,uid;
  1070.   UBYTE attr;
  1071.   WORD i;
  1072.  
  1073.   /* Remove old entry */
  1074. #ifdef FILEDEBUG
  1075.   Printf("DeleteDB %ld\n",plfh->dbinfo.name);
  1076. #endif
  1077.   DLP_DeleteDB(socket,cardno,plfh->dbinfo.name);
  1078.  
  1079.   /* Calculate the flags */
  1080.   tmpflags = plfh->dbinfo.flags;
  1081.   if(strcmp(plfh->dbinfo.name,"Graffiti ShortCuts")==0)
  1082.   {
  1083.     tmpflags |= DLPDBIF_OPEN;   // A file that is already opened, will be changed
  1084.     reset = TRUE;               // Just to make sure...
  1085.   }
  1086.  
  1087. #ifdef FILEDEBUG
  1088.   Printf("CreateDB %ld\n",plfh->dbinfo.name);
  1089. #endif
  1090.   dbh = DLP_CreateDB(socket,cardno,plfh->dbinfo.creator,plfh->dbinfo.type,tmpflags,plfh->dbinfo.version,plfh->dbinfo.name);
  1091.   if(dbh>=0)
  1092.   {
  1093.     appinfo = PL_FileGetAppInfo(fh,&size);
  1094. #ifdef FILEDEBUG
  1095.   Printf("GetAppInfo 0x%08lx\n",appinfo);
  1096. #endif
  1097.     /* Workaround for a bug in PalmOS 2.0 */
  1098.     if((sock->version>0x0100) && (strcmp(plfh->dbinfo.name,"MemoDB")==0) && (size>0) && (size<282))
  1099.     {
  1100.       if(appinfo2 = AllocVec(282,MEMF_PUBLIC))
  1101.       {
  1102.         CopyMem(appinfo,appinfo2,size);
  1103.         appinfo = appinfo2;
  1104.         size = 282;
  1105.       }
  1106.     }
  1107.  
  1108.     if(plfh->dbinfo.creator == MKTAG('p','t','c','h')) reset = TRUE;
  1109.     if(plfh->dbinfo.flags & DLPDBIF_RESET) reset = TRUE;
  1110.  
  1111. #ifdef FILEDEBUG
  1112.   Printf("WriteAppBlock 0x%08lx\n",appinfo);
  1113. #endif
  1114.     if(size>0) DLP_WriteAppBlock(socket,dbh,appinfo,size);
  1115.  
  1116.     if(plfh->dbinfo.flags & DLPDBIF_RESOURCE)
  1117.     {
  1118.       /*--- INSTALL RESOURCE ---*/
  1119.       for(i=0; i<plfh->cntEntries; i++)
  1120.       {
  1121.         buffer = PL_FileReadResource(fh,i,&size,&type,&id);
  1122. #ifdef FILEDEBUG
  1123.   Printf("ReadResource 0x%08lx\n",buffer);
  1124. #endif
  1125.         if(!buffer || size>65535) goto error;                               // ERROR: No Resource, or >64K
  1126. #ifdef FILEDEBUG
  1127.   Printf("WriteResource 0x%08lx\n",buffer);
  1128. #endif
  1129.         if(!DLP_WriteResource(socket,dbh,type,id,buffer,size)) goto error;  // Failure
  1130.         if(type == MKTAG('b','o','o','t')) reset = TRUE;
  1131.       }
  1132.       res = TRUE;
  1133.     }
  1134.     else
  1135.     {
  1136.       /*--- INSTALL RECORD ---*/
  1137.       for(i=0; i<plfh->cntEntries; i++)
  1138.       {
  1139.         buffer = PL_FileReadRecord(fh,i,&size,&attr,&uid);
  1140. #ifdef FILEDEBUG
  1141.   Printf("ReadRecord 0x%08lx\n",buffer);
  1142. #endif
  1143.         if(!buffer || size>65535) goto error;                               // ERROR: No Record, or >64K
  1144.         if((attr&DLPDBAT_DELETED) && (sock->version<0x0101)) continue;      // PalmOS1 cannot install deleted entries
  1145. #ifdef FILEDEBUG
  1146.   Printf("WriteRecord 0x%08lx\n",buffer);
  1147. #endif
  1148.         if(!DLP_WriteRecord(socket,dbh,attr&0xF0,uid,attr&0x0F,buffer,size,NULL)) goto error;  // Failure
  1149.       }
  1150.       res = TRUE;
  1151.     }
  1152.  
  1153. error:
  1154.     if(reset) DLP_ResetSystem(socket);
  1155. #ifdef FILEDEBUG
  1156.   Printf("CloseDB\n",buffer);
  1157. #endif
  1158.     DLP_CloseDB(socket,dbh);
  1159.     if(!res) DLP_DeleteDB(socket,cardno,plfh->dbinfo.name);   // Remove faulty entry
  1160.   }
  1161.  
  1162.   if(appinfo2) FreeVec(appinfo2);
  1163.   return(res);
  1164. }
  1165. //<
  1166. /*------------------------------------------------------**
  1167. ** Name:        PL_FileMerge
  1168. **
  1169. ** Funktion:    Verbindet eine Datei mit einer Palm-Datenbank
  1170. **
  1171. ** Parameter:   fh        Filehandle
  1172. **              socket    Socket
  1173. **              cardno    Karten-Nr
  1174. ** Ergebnis:    success
  1175. //>
  1176. ** Bemerkungen:
  1177. **
  1178. ** Revision:    14. Juni 1998, 23:05:50
  1179. */
  1180. __saveds __asm int PL_FileMerge
  1181. (
  1182.   register __a0 APTR fh,
  1183.   register __a1 APTR socket,
  1184.   register __d0 UBYTE cardno
  1185. )
  1186. {
  1187.   struct PL_File *plfh = (struct PL_File *)fh;
  1188.   struct PL_Socket *sock = (struct PL_Socket *)socket;
  1189.   int  res = FALSE;
  1190.   BOOL reset = FALSE;
  1191.   LONG dbh;
  1192.   UWORD id;
  1193.   APTR  buffer;
  1194.   ULONG size,type,uid;
  1195.   UBYTE attr;
  1196.   WORD  i;
  1197.  
  1198.   dbh = DLP_OpenDB(socket,cardno, DLPDBOF_READWRITE|DLPDBOF_SECRET, plfh->dbinfo.name);
  1199.   if(dbh<0) return(PL_FileInstall(fh,socket,cardno));   // Just install
  1200.  
  1201.   if(plfh->dbinfo.creator == MKTAG('p','t','c','h')) reset=TRUE;
  1202.   if(plfh->dbinfo.flags & DLPDBIF_RESET) reset = TRUE;
  1203.  
  1204.   if(plfh->dbinfo.flags & DLPDBIF_RESOURCE)
  1205.   {
  1206.     /*--- INSTALL RESOURCE ---*/
  1207.     for(i=0; i<plfh->cntEntries; i++)
  1208.     {
  1209.       buffer = PL_FileReadResource(fh,i,&size,&type,&id);
  1210.       if(!buffer || size>65535) goto error;                               // ERROR: No Resource, or >64K
  1211.       if(!DLP_WriteResource(socket,dbh,type,id,buffer,size)) goto error;  // Failure
  1212.       if(type == MKTAG('b','o','o','t')) reset = TRUE;
  1213.     }
  1214.     res = TRUE;
  1215.   }
  1216.   else
  1217.   {
  1218.     /*--- INSTALL RECORD ---*/
  1219.     for(i=0; i<plfh->cntEntries; i++)
  1220.     {
  1221.       buffer = PL_FileReadRecord(fh,i,&size,&attr,&uid);
  1222.       if(!buffer || size>65535) goto error;                               // ERROR: No Record, or >64K
  1223.       if((attr&DLPDBAT_DELETED) && (sock->version<0x0101)) continue;      // PalmOS1 cannot install deleted entries
  1224.       if(!DLP_WriteRecord(socket,dbh,attr&0xF0,uid,attr&0x0F,buffer,size,NULL)) goto error;  // Failure
  1225.     }
  1226.     res = TRUE;
  1227.   }
  1228.  
  1229. error:
  1230.   if(reset) DLP_ResetSystem(socket);
  1231.   DLP_CloseDB(socket,dbh);
  1232.   return(res);
  1233. }
  1234. //<
  1235.  
  1236.  
  1237.  
  1238. /********************************************************************/
  1239.